<html>
    <head>
        <title>工作单元</title>
    </head>
    <body>
        <script>
            //  工作单元

                    /*
                            ABP的工作单元（UOW）实现提供了对应用程序中数据库连接和事务作用域的抽象和控制。

                            一旦一个新的UOW启动，它 就会创建一个作用域范围，该作用域范围内执行的所有数据库操作都将参与同一个事务，并被视为"单个事务边界"。

                            这些操作员一起被提交(成功时) 或 一起被回滚(失败时)。


                            ABP的UOW系统的创建和运行一般是：

                                ①、根据约定的，所以大多数时间你根部不用处理UOW。

                                ②、独立于数据库提供程序的

                                ③、Web独立，这意味着你可以在Web应用程序/服务之外的任何类型的应用程序中创建工作单元范围。

                    */


                    /*
                        一、工作单元相关公约

                                1、ABP 为哪些方法类型自动启动UOW

                                                以下方法类型被视为一个工作单元：
                                                
                                                                ①、asp.net core 的控制器 Action方法

                                                                ②、ASP.NETCore Razor页面处理程序。

                                                                ③、Application service methods 应用服务方法。

                                                                ④、Repository methods. 存储库方法
                                                    
                                                对于这些方法，UOW会自动开始，除非已经有工作单元的作用域在起作用。

                                                例子：

                                                    ①、如果你调用一个存储库方法并且还没有启动UOW，它会自动开始一个新的事务性UOW，它涉及存储库方法中完成的所有操作，如果存储库方法没有抛出任何异常，会提交事务。
                                                
                                                        存储库方法根本不知道UOW或事务。它只适用于常规数据库对象（例如，EF Core的DbContext），UOW由ABP处理。


                                                    ②、如果你调用应用程序服务方法，则相同的UOW系统如上所述工作。

                                                        如果应用程序服务方法使用某些存储库，则存储库不会开始新的UOW，但参与由ABP为应用程序服务方法启动的当前工作单元。

                                                    
                                                    ③、ASP.NETCore控制器动作也是如此。如果操作以控制器动作开始，则UOW范围是控制器动作的方法体。

                                                所有这些都由ABP自动处理。




                                2、数据库事务行为  Database Transaction Behavior

                                                 虽然上面的部分解释了UOW，因为它是数据库事务，但实际上UOW不一定是事务性的。默认情况下：

                                                    ①、HTTP GET请求不会启动事务性UOW。它们仍然启动UOW，但不会创建数据库事务。

                                                    ②、如果底层"数据库提供程序"支持数据库级事务,则所有其他HTTP请求类型都使用数据库事务启动UOW。


                                                这是因为HTTP GET 请求不会(也不应该)在数据库中进行任何更改。你可以使用下面的解释的选项更改此行为。

                    */

                    /*
                        二、默认选项类    AbpUnitOfWorkDefaultOptions默认选项类

                                    AbpUnitOfWorkDefaultOptions用于配置工作单元系统的默认选项。

                                    在模块的ConfigureServices方法中配置选项。

                                    eg:  完全禁用数据库事务

                                            Configure<AbpUnitOfWorkDefaultOptions>(options =>
                                            {
                                                options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled;
                                            });

                                    可配置的选项属性如下：

                                            ①、TransactionBehavior（enum：UnitOfWorkTransactionBehavior）。

                                                        用于配置事务行为的全局属性。默认值为Auto，如上面的“数据库事务行为”部分所述。
                                                        
                                                        你可以使用此选项启用（即使对于HTTP GET请求）或禁用事务。

                                            ②、TimeOut（int?）：用于设置UOW的超时值。默认值为null并使用底层数据库提供程序的默认值。

                                            ③、IsolationLevel（IsolationLevel?）：用于设置数据库事务的隔离级别，如果UOW是事务性的。
                    */

                    /*
                        三、控制类、方法工作单元  Controlling the Unit Of Work

                                   在某些情况下，你可能希望更改常规事务范围、创建内部范围或精细控制事务行为。以下部分涵盖了这些可能性。

                                   1、IUnitOfWorkEnable接口  

                                                这是一种简单的方法，可以为不是上面解释的约定的工作单元的类（或类的层次结构）启用UOW。

                                                eg: 为任意服务实现IUnitOfWorkEnabled

                                                    using System.Threading.Tasks;
                                                    using Volo.Abp.DependencyInjection;
                                                    using Volo.Abp.Uow;

                                                    namespace AbpDemo
                                                    {
                                                        // 实现IUnitOfWorkEnabled接口后，该类所有方法都将在一个UOW作用域内执行。
                                                        public class MyService : ITransientDependency, IUnitOfWorkEnabled 
                                                        {
                                                            // 方法必须为虚方法，才能在"非注入"情况下启用UOW。
                                                            // 方法必须为异步方法，否则UOW无法工作。
                                                            public virtual async Task FooAsync()
                                                            {
                                                                // 此方法将在一个UOW作用域内执行。
                                                            }
                                                        }
                                                    }
                                                
                                                然后MyService（以及从中派生的任何类）方法将是UOW。

                                                但是，应该遵循一些规则才能使其工作：

                                                        ①、如果你没有通过接口注入服务(eg: IMyService) ,则该服务的方法必须是 virtual 否则，动态代理/拦截系统无法工作）。

                                                        ②、只有async方法（返回Task或Task<T>的方法）被拦截。因此，同步方法不能启动UOW。

                                                        
                                                （请注意，如果在UOW范围内调用了FooAsync，则它已经参与到UOW中，而不需要IUnitOfWorkEnabled或任何其他配置。）

                                    2、[UnitOfWork] 特性   

                                            (1)、如何使用[UnitOfWork]特性为类、方法启用UOW？

                                                        [UnitOfWork]特性提供了更多的可能性，例如启用或禁用UOW以及控制事务行为。

                                                        [UnitOfWork]特性可用于"类" 或 "方法" 级别。

                                                        eg： 为类的特定方法启用UOW
                                                                    
                                                            using System.Threading.Tasks;
                                                            using Volo.Abp.DependencyInjection;
                                                            using Volo.Abp.Uow;

                                                            namespace AbpDemo
                                                            {
                                                                public class MyService : ITransientDependency
                                                                {
                                                                    [UnitOfWork]  // 使用特性为方法启用UOW
                                                                    public virtual async Task FooAsync()  // 注意，必须是async方法，必须是virtual方法 否则无法启用UOW
                                                                    {
                                                                        //this is a method with a UOW scope
                                                                    }
                                                                    
                                                                    public virtual async Task BarAsync()
                                                                    {
                                                                        //this is a method without UOW
                                                                    }
                                                                }
                                                            }

                                                    
                                                        eg: 为类的所有方法启用UOW

                                                            using System.Threading.Tasks;
                                                            using Volo.Abp.DependencyInjection;
                                                            using Volo.Abp.Uow;

                                                            namespace AbpDemo
                                                            {
                                                                [UnitOfWork]  // 使用特性为类启用UOW
                                                                public class MyService : ITransientDependency
                                                                {
                                                                    public virtual async Task FooAsync() // 注意，必须是async方法，必须是virtual方法 否则无法启用UOW
                                                                    {
                                                                        //this is a method with a UOW scope
                                                                    }
                                                                    
                                                                    public virtual async Task BarAsync()
                                                                    {
                                                                        //this is a method with a UOW scope
                                                                    }
                                                                }
                                                            }

                                                    ★、同样，相同的规则在这里有效：

                                                            ①、如果你没有通过接口注入服务（如IMyService），则服务的方法必须是virtual（否则，动态代理/拦截系统无法工作）。

                                                            ②、只有async方法（返回Task或Task<T>的方法）被拦截。因此，同步方法不能启动UOW。



                                            (2)、[UnitOfWork]特性的的属性   [UnitOfWorkAttribute] Properties 

                                                        ①、IsTransactional（bool?）：用于设置UOW是否应该是事务性的。默认值为null。如果你让它null，它会根据约定和配置自动确定。

                                                        ②、TimeOut（int?）：用于设置此UOW的超时值。默认值为null并回退到默认配置值。

                                                        ③、IsolationLevel（IsolationLevel?）：用于设置数据库事务的隔离级别，如果UOW是事务性的。如果未设置，则使用默认配置值。

                                                        ④、IsDisabled（bool）：用于禁用当前方法/类的UOW。

                                        ★、 
                                            (如果一个方法在UOW作用域内部调用，那么它自身的[UnitOfWork]特性将被忽略，它自动加入调用者的UOW作用域。
                                            
                                              被调用的方法不再开启新的UOW作用域。)           

                                                        eg: 为控制器操作禁用UOW

                                                                using System.Threading.Tasks;
                                                                using Volo.Abp.AspNetCore.Mvc;
                                                                using Volo.Abp.Uow;

                                                                namespace AbpDemo.Web
                                                                {
                                                                    public class MyController : AbpController
                                                                    {
                                                                        [UnitOfWork(IsDisabled = true)]   // 禁用该action的UOW
                                                                        public virtual async Task FooAsync()
                                                                        {
                                                                            //...
                                                                        }
                                                                    }
                                                                }
                    */

                    /*
                         四、工作单元管理器  IUnitOfWorkManager

                                    IUnitOfWorkManager是用于控制工作单元系统的主要服务。

                                    以下部分解释了如何直接使用此服务（而大多数时候你不需要）。



                                    1、开始一个新的工作单元   Begin a New Unit Of Work 

                                            IUnitOfWorkManager.Begin方法用于创建新的UOW范围。

                                            eg:  创建一个新的"非事务性"UOW范围

                                                using System.Threading.Tasks;
                                                using Volo.Abp.DependencyInjection;
                                                using Volo.Abp.Uow;

                                                namespace AbpDemo
                                                {
                                                    public class MyService : ITransientDependency
                                                    {
                                                        private readonly IUnitOfWorkManager _unitOfWorkManager;

                                                        public MyService(IUnitOfWorkManager unitOfWorkManager)
                                                        {
                                                            _unitOfWorkManager = unitOfWorkManager;
                                                        }
                                                        
                                                        public virtual async Task FooAsync()
                                                        {
                                                            using (var uow = _unitOfWorkManager.Begin( // 开启新的UOW范围，并且是非事务性的

                                                                requiresNew: true, isTransactional: false
                                                            ))
                                                            {
                                                                //...
                                                                
                                                                await uow.CompleteAsync();
                                                            }
                                                        }
                                                    }
                                                }

                                                ★、Begin方法获取以下可选参数：

                                                        ①、requiresNew（bool）：设置true以忽略周围的工作单元并使用提供的选项启动一个新的UOW。

                                                                                默认值为false。
                                                                                
                                                                                如果它是false并且有一个周围的UOW，Begin方法实际上并没有开始一个新的UOW，而是静默地参与现有的UOW。

                                                        ②、isTransactional（bool）。默认值为false。是否为事务性的UOW。
                                                        
                                                        ③、isolationLevel（IsolationLevel?）：用于设置数据库事务的隔离级别，如果UOW是事务性的。如果未设置，则使用默认配置值。

                                                        ④、TimeOut（int?）：用于设置此UOW的超时值。默认值为null并回退到默认配置值。



                                    2、当前的工作单元   The Current Unit Of Work

                                                如前面所述，UOW是代码无感知的。

                                              (1)、如何获取当前工作单元？

                                                            如果你需要访问当前工作单元，你可以使用IUnitOfWorkManager.Current属性。

                                                            eg: 获取当前UOW

                                                                using System.Threading.Tasks;
                                                                using Volo.Abp.DependencyInjection;
                                                                using Volo.Abp.Uow;

                                                                namespace AbpDemo
                                                                {
                                                                    public class MyProductService : ITransientDependency
                                                                    {
                                                                        private readonly IUnitOfWorkManager _unitOfWorkManager;

                                                                        public MyProductService(IUnitOfWorkManager unitOfWorkManager)
                                                                        {
                                                                            _unitOfWorkManager = unitOfWorkManager;
                                                                        }
                                                                        
                                                                        public async Task FooAsync()
                                                                        {
                                                                            var uow = _unitOfWorkManager.Current; // 获取当前UOW

                                                                            //...
                                                                        }
                                                                    }
                                                                }

                                                            ★、Current属性返回一个IUnitOfWork对象。

                                                            备注：

                                                            如果当前作用域没有Uow，当前工作单元可以是null.

                                                            如果你的类是传统的UOW类，则不会是null，你手动将其设为UOW或在UOW范围内调用，如前所述。
                                                   

                                                (2)、异步保存更改 SaveChangesAsync

                                                            IUnitOfWork.SaveChangesAsync()方法将所有更改保存到数据库中。

                                                            如果你使用的是EF Core，它的行为完全相同。

                                                            如果当前UOW是事务性的，即使保存的更改也可以在错误时回滚（对于支持的数据库提供程序）。

                                                            eg: 插入实体以获取其自动增量ID后保存更改
                                                   

                                                                using System.Threading.Tasks;
                                                                using Volo.Abp.Application.Services;
                                                                using Volo.Abp.Domain.Repositories;

                                                                namespace AbpDemo
                                                                {
                                                                    public class CategoryAppService : ApplicationService, ICategoryAppService
                                                                    {
                                                                        private readonly IRepository<Category, int> _categoryRepository;

                                                                        public CategoryAppService(IRepository<Category, int> categoryRepository)
                                                                        {
                                                                            _categoryRepository = categoryRepository;
                                                                        }

                                                                        public async Task<int> CreateAsync(string name)
                                                                        {
                                                                            var category = new Category {Name = name};
                                                                            await _categoryRepository.InsertAsync(category); 
                                                                            
                                                                            // 保存更改，以便获取自动递增的ID
                                                                            await UnitOfWorkManager.Current.SaveChangesAsync();
                                                                            
                                                                            return category.Id;
                                                                        }
                                                                    }
                                                                }

                                                            此示例对Category实体使用自动增量int主键。自动增量PK"需要将实体保存到数据库"以获取新实体的id。
                                                        
                                                            此示例是从基ApplicationService类派生的应用程序服务，

                                                            该类已经将IUnitOfWorkManager服务作为UnitOfWorkManager属性注入。因此，无需手动注入。

                                                            由于获取当前UOW非常常见，因此还有一个CurrentUnitOfWork属性作为UnitOfWorkManager.Current的快捷方式。

                                                            因此，可以更改上面的示例以使用它：

                                                                await CurrentUnitOfWork.SaveChangesAsync();


                                                            ★、保存更改的替代方案 

                                                                        由于经常需要在插入、更新或删除实体后保存更改，因此相应的repository方法有一个可选的autoSave参数。

                                                                        因此，上面的CreateAsync方法可以重新编写，如下所示：

                                                                            public async Task<int> CreateAsync(string name)
                                                                            {
                                                                                var category = new Category {Name = name};
                                                                                await _categoryRepository.InsertAsync(category, autoSave: true); // 自动保存更改

                                                                                return category.Id;
                                                                            }



                                                            ★、如果你的意图只不过是在创建、修改、删除后保存更改，建议你使用autoSave 选项就好，而不是使用 CurrentUnitOfWork.SaveChangesAsync:

                                                            

                                                            ★、备注：

                                                                    ①、当一个工作单元结束时，所有更改都会自动保存而没有任何错误。
                                                                    
                                                                        所以，除非你真的需要，否则不要调用SaveChangesAsync()，也不要将autoSave设置为true。

                                                                    ②、如果使用Guid作为主键，则无需在插入时保存更改即可获取生成的id，因为Guid键在应用程序中设置，并且在创建新实体后立即可用。


                                                    (3)、IUnitOfWork（工作单元接口）的其他方法和属性

                                                                    ①、OnCompleted方法获取一个回调操作，该操作在工作单元成功完成时调用（你可以确定所有更改都已保存）。

                                                                    ②、Failed和Disposed事件可用于在UOW失败或被释放时通知。

                                                                    ③、Complete和Rollback方法用于完成（提交）或回滚当前UOW，这通常由ABP内部使用，
                                                                    
                                                                                        但如果你使用IUnitOfWorkManager.Begin方法手动启动事务，则可以使用。

                                                                    ④、Options可用于获取启动UOW时使用的选项。

                                                                    ⑤、Items字典可用于存储和获取同一工作单元内的任意对象，这可以成为实现自定义逻辑的一个点。

                    */

                    /*
                        五、asp.net 集成

                                  工作单元系统完全集成到ASP.NET core。 
                                  
                                  当你使用ASP.NET core MVC控制器或 Razor页面时，它可以正常工作。

                                  它为UOW系统定义了操作过滤器和页面过滤器。

                                  (当你使用ASP.NET核心时，你通常不需要配置UOW。)



                              1、工作单元中间件

                                      AbpUnitOfWorkMiddleware是一个中间件，可以在ASP.NET核心请求管道中启用UOW。
                                      
                                      如果你需要扩大UOW范围以涵盖其他一些中间件，这可能是必需的。    
                                      
                                      eg:

                                            app.UseUnitOfWork();
                                            app.UseConfiguredEndpoints();
                    */

        </script>
    </body>
</html>